import method from "@mac-xiang/method";

import { Create, oa, SqlObject, os, ck, typeField, ns, typeFieldMember } from "./itf";
import { Where, curd, Literal } from "./struct";
import { clearEscapeChar } from "./fun";


export default class TmkMysql {
  field: typeField = {};
  table: string = "";
  limit: number = 100;
  offset: number = 0;
  auto: string[] = ["id"];
  read: string[] = ["id"];
  fk: string[] = [];
  showField: string[] = [];
  readonly check: boolean = false;
  constructor (param: Create) {
    if (typeof param.field == "object" && method.isStringT(param.table)) {
      this.field = param.field;
      this.fk = Object.keys(this.field);
      this.table = param.table;

      if (method.isInt(param.limit)) this.limit = parseInt((param.limit as number).toString());
      if (method.isInt(param.offset)) this.offset = parseInt((param.offset as number).toString());
      if (Array.isArray(param.auto)) {
        const d: string[] = [];
        param.auto.map(a => {
          if (method.isStringT(a) && this.fk.indexOf(a) >= 0) {
            d.push(a);
          }
        });
        this.auto = d;
      }
      if (Array.isArray(param.read)) {
        const d: string[] = [];
        param.read.forEach(a => {
          if (method.isStringT(a) && this.fk.indexOf(a) >= 0) {
            d.push(a);
          }
        });
        this.read = d;
      }
      if (Array.isArray(param.showField)) {
        param.showField.forEach(a => {
          if (typeof a == "string" && this.field[a]) {
            this.showField.push(a);
          }
        });
      } else this.showField = Object.keys(this.field);
    }
  }
  private Where2s(p: Where[]) {
    const ra: string[] = p.map(w => {
      return w.string;
      // return `${w.logic} \`${w.field}\` ${w.method} ${w.value}`;
    });
    let ret = ra.join(" ");
    while (ret.substr(0, 1) == " ") {
      ret = ret.substr(1);
    }
    let s = 0;
    if (ret.substr(0, 3) == "or ") {
      s = 2;
    } else if (ret.substr(0, 4) == "and ") {
      s = 3;
    }
    return ret.substr(s);
  }
  private value2s(value: os, insert: boolean = false) {
    let ret: string = "";
    if (insert) {
      const f = this.fk.filter(a => {
        return this.auto.indexOf(a) < 0;
      });
      const v = f.map(a => {
        return this.formatValue(value[a], this.field[a]);
      });
      ret = `(\`${f.join("`,`")}\`) VALUES (${v.join(",")})`;
    } else {
      const ra: string[] = Object.keys(value).map(w => {
        return `\`${w}\`=${this.formatValue(value[w], this.field[w])}`;
      });
      ret = ra.join(",");
    }
    return ret;
  }
  private SqlObject2s(fmt: SqlObject) {
    let ret: string = curd[fmt.method] as string;
    let w = this.Where2s(fmt.where);
    if (w) w = "WHERE" + w;
    const v = this.value2s(fmt.value, ret == curd[2]);
    switch (ret) {
      case curd[0]: // 读
        ret = `${ret} \`${fmt.field.join("`,`")}\` FROM \`${fmt.table}\` ${w} ${fmt.other} LIMIT ${fmt.offset},${fmt.limit}`;
        break;
      case curd[1]: // 改
        ret = v ? `${ret} \`${fmt.table}\` SET ${v} ${w}` : "";
        break;
      case curd[2]: // 增
        ret = v ? `${ret} \`${fmt.table}\` ${v}` : "";
        break;
      case curd[3]: // 删
        ret = w ? `${ret} \`${fmt.table}\` ${w}` : "";
        break;

      default:
        ret = "";
        break;
    }
    return ret;
  }
  getMethod(p: any) {
    let ret: ck = curd[p as ck] as ck;
    if (!ret) ret = 0;
    else if (typeof ret == "string") ret = p as ck;
    return ret;
  }
  getWhere(p: any) {
    let r: Where[] = [];
    if (Array.isArray(p)) {
      p.forEach(a => {
        const e = new Where(a, this.field);
        if (e.check && (this.fk.indexOf(e.field) >= 0 || !e.field)) r.push(e);
      });
    }
    return r;
  }
  private formatValue(p: any, f: typeFieldMember) {
    let r: string | number = "";
    if (typeof p == "undefined") {
      r = f.length == 3 ? f[0] : `'${clearEscapeChar(p)}'`;
    } else {
      if (p instanceof Literal) {
        r = p.value;
      } else if (f.length == 2) {
        let s = clearEscapeChar(p);
        s = s.substr(0 - f[1]);
        r = `'${s}'`;
      } else {
        const n = Number(p) ? Number(p) : f[0];
        const f2 = f[2] as number;
        if (f[1] <= n && n <= f2) r = n;
        else r = f[0];
      }
    }
    return r;
  }
  getValue(p: any, m: any) {
    const r: oa = {};
    if (method.type(p) == "object") {
      const read = this.fk.filter(k => {
        const f = this.field[k];
        if ((m == 2 || (m == 1 && typeof p[k] != "undefined")) &&
          this.auto.indexOf(k) < 0) {
          // p[k] = this.formatValue(p[k], f);
          return true;
        }
      });
      Object.keys(p).forEach(k => {
        if (read.indexOf(k) >= 0) r[k] = p[k];
        // if (read.indexOf(k) >= 0) {
        //   if (p[k] instanceof Literal) {
        //     r[k] = p[k].value;
        //   } else {
        //     if (this.field[k].length > 2) {
        //       r[k] = Number(p[k]);
        //       if (!(r[k] >= 0 || r[k] < 0)) r[k] = this.field[k][0];
        //     } else {
        //       r[k] = `'${clearEscapeChar(p[k])}'`;
        //     }
        //   }
        // }
      });
    }
    return r;
  }
  getField(p: any) {
    let r: string[] = this.showField;
    if (Array.isArray(p)) {
      r = [];
      p.forEach(a => {
        if (this.field[a]) r.push(a);
      });
    }
    return r;
  }
  sql(p: oa) {
    p.method = this.getMethod(p.method);
    const fmt: SqlObject = {
      method: p.method,
      table: (p.table instanceof Literal) ? p.table.value : this.table,
      where: this.getWhere(p.where),
      value: this.getValue(p.value, p.method),
      limit: method.isUint(p.limit) ? p.limit : this.limit,
      offset: method.isUint(p.offset) ? p.offset : this.offset,
      field: this.getField(p.field),
      other: p.other instanceof Literal ? p.other.value : ""
    };
    return this.SqlObject2s(fmt);
  }
}

export { Literal, TmkMysql };